package hyPeerDriveTests;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Iterator;

import org.junit.Test;

import phase4Specification.testing.DepositBroadcastVisitor;
import phase4Specification.testing.TrackSendPathVisitor;
import dbPhase.hypeerweb.HyPeerWeb;
import dbPhase.hypeerweb.HyPeerWebDatabase;
import dbPhase.hypeerweb.Node;
import dbPhase.hypeerweb.Parameters;
import dbPhase.hypeerweb.SendVisitor;
import dbPhase.hypeerweb.SimplifiedNodeDomain;

public class ChaseTests {
    public static HyPeerWeb hypeerweb = null;
    /*
     * Testing Send
     * 
     * Equivalence partitioning
     * 
     * Equivalence in size of hypeerweb when sending
     * 1. Empty hypeerweb
     * 2. Hypeerweb of size 1
     * 3. Hypeerweb of size 2, 4, 8, 16, 32 (Cap)
     * 4. Hypeerweb of size between 3 and 31
     * 
     */
    @Test
    public void test() {
        HyPeerWebDatabase.initHyPeerWebDatabase();
        HyPeerWebDatabase.getSingleton().clear();
        hypeerweb = HyPeerWeb.getSingleton();
        // 1.) the assert below has an error you try to make a send even when the hypeerweb is empty.(gives stack overflow)
        //assertTrue(testSend(0,0,0));
        // 2.) 
        assertTrue(testSend(1,0,0));
        // 3.)
        assertTrue(testSend(2,0,1));
        //using perfect hypeercube can you make it all around.
        assertTrue(testSend(4,0,3));
        assertTrue(testSend(4,0,2));
        assertTrue(testSend(4,0,1));
        assertTrue(testSend(4,0,0));
        //using perfect hypeercube can you make it starting from random nodes including the final node.
        assertTrue(testSend(4,0,3));
        assertTrue(testSend(4,1,2));
        assertTrue(testSend(4,1,3));
        assertTrue(testSend(4,2,1));
        assertTrue(testSend(5,4,1));
        // 4.)
        //Hypeerweb size 3.
        assertTrue(testSend(3,0,2));
        assertTrue(testSend(3,0,1));
        assertTrue(testSend(3,2,0));
        //Hypeerweb size 31
        //can you make it to last node.
        assertTrue(testSend(31,0,30));
        //can you make it to last node not starting on 0
        assertTrue(testSend(31,1,30));
        //can you make it to first node starting from last node.
        assertTrue(testSend(31,30,0));
        //can you make it starting from a random node to another random node.
        assertTrue(testSend(31,5,15));
        
        TrackSendPathVisitor trackSendPathVisitor = new TrackSendPathVisitor();
        Node node = new Node(0);
        node.contents.set("paths", new ArrayList<ArrayList<Integer>>());
        Parameters p = SendVisitor.createInitialParameters(0);
        p.set("path", new ArrayList<Integer>());
        trackSendPathVisitor.visit(node, p);
    }
    static private void createHyPeerWebWith(int numberOfNodes) {
        hypeerweb.clear();
        Node node0 = new Node(0);
        hypeerweb.addToHyPeerWeb(node0, null);

        for (int i = 1; i < numberOfNodes; i++) {
            Node node = new Node(0);
            node0.addToHyPeerWeb(node);
        }
    }
    
    private static boolean sendVisitorErrorHeaderPrinted = false;

    @SuppressWarnings("unchecked")
    private static boolean testSend(int hypeerwebSize, int start,  int target) {
        createHyPeerWebWith(hypeerwebSize);

        TrackSendPathVisitor trackSendPathVisitor = new TrackSendPathVisitor();
        Parameters parameters = SendVisitor.createInitialParameters(target);
        parameters.set(SendVisitor.TARGET_KEY, target);
        ArrayList<Integer> emptyList = new ArrayList<Integer>();
        parameters.set(TrackSendPathVisitor.PATH_KEY, emptyList);
        hypeerweb.getNode(start).accept(trackSendPathVisitor, parameters);

        ArrayList<ArrayList<Integer>> finalResult = (ArrayList<ArrayList<Integer>>) hypeerweb.getNode(target).getContents().get(TrackSendPathVisitor.PATHS_KEY);
        return checkFinalResult(finalResult, target);
    }
    
    private static boolean checkFinalResult(ArrayList<ArrayList<Integer>> paths, int target) {
        boolean result = true;
        for (ArrayList<Integer> path : paths) {
            //System.out.println("path"+path);
            int loc = checkFinalResultz(path, target);
            if (loc != -1) {
                result = false;
                if (!sendVisitorErrorHeaderPrinted) {
                    System.out.println("-------------------------------------------------------------");
                    System.out.println("Errors encountered while testing the SendVisitor");
                }
                sendVisitorErrorHeaderPrinted = false;
                System.out.print("    For the path: ");
                for (Integer i : path) {
                    System.out.print(i + " ");
                }
                System.out.println(" there was a better choice than going from node "    + path.get(loc) + " to " + path.get(loc + 1));
                System.out.println("    This was the first incorrect choice detected, there may be others");
            }
        }
        return result;
    }

    private static int checkFinalResultz(ArrayList<Integer> path, int targetId) {
        int result = -1;
        for (int i = 0; result == -1 && i < path.size() - 1; i++) {
            //System.out.print(result);
            if (!isBestChoice(path.get(i), path.get(i + 1), targetId)) {
                result = i;
            }
        }
        return result;
    }

    private static boolean isBestChoice(int source, int nextNode, int targetId) {
        SimplifiedNodeDomain sourceNode = hypeerweb.getNode(source).constructSimplifiedNodeDomain();
        boolean result = !sourceNode.containsCloserNode(nextNode, targetId);
        return result;
    }
    
    private static ArrayList<Integer> checkNodes(int i) {
        ArrayList<Integer> nodesNotVisited = new ArrayList<Integer>();

        int index = 0;
        Iterator<Node> iter = hypeerweb.iterator();
        while(iter.hasNext()){
            Node node = iter.next();
            if ((Integer) node.getContents().get(DepositBroadcastVisitor.DEPOSIT_KEY) != i) {
                nodesNotVisited.add(index);
            }
            index++;
        }

        return nodesNotVisited;
    }

    private static final int HYPEERWEB_SIZE = 32;
    private static final boolean VERBOSE = false;

}
